/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Mark
 * Started 5/16/08
 */
package gfs;

import java.awt.Color;
import java.util.ArrayList;


public class StrategoSoldier extends Object implements Comparable
    {
    
    public enum Type {SOLDIER, FLAG, BOMB};
    protected int pieceVal;
    protected String name;
    protected Type type;
    protected Coordinate cd;
    protected Color col;
    protected int numBattles;
    protected Board board;
    
    public StrategoSoldier(String name, Type tag, Coordinate cd, 
            Color cl, Board bd)
        {
        this(99, name, tag, cd, cl, bd);
        }
    
    public StrategoSoldier(int pV, String name, Type tag, Coordinate cd
            , Color cl, Board bd)
        {
        pieceVal = pV;
        this.name = name;
        this.cd = cd;
        this.col = cl;
        board = bd;
        setColor(cl);
        type = tag;
        numBattles = 0;
        }
            
    public void moveLoc(Coordinate loc, boolean showString)
        {
        if(board.isEmpty(loc, true))
            {
            this.erase();
            this.setCoordinate(loc);
            this.draw(showString);
            }
        }
            
    /**
     * Compares this <code>StrategoSoldier</code> to another 
     * <code>StrategoSoldier</code>
     * @param o 
     *      The soldier object to compare this soldier to
     * @return
     *      1 if this soldier is greater than the opposing soldier. 0 if this 
     * soldier is equal to the opposing soldier. -1 if this soldier is less than
     * the opposing soldier.
     */
    public int compareTo(Object o) 
        {
        int comp = -1;
        StrategoSoldier soldier = (StrategoSoldier)o;
        if(type == Type.SOLDIER)            
            if(soldier.getType() == Type.BOMB)
                comp = !(this instanceof StrategoMiner) ? -1 : 1;
            else
                {
                if(!(this instanceof StrategoSpy))                    
                    comp = soldier.getVal() - getVal();
                else
                    if(soldier instanceof StrategoMarshal)
                        comp = 1;
                    else 
                        comp = soldier instanceof StrategoSpy ? 0 : -1;
                }                 
        if(type == Type.BOMB)
            comp = soldier instanceof StrategoMiner ? -1 : 1;
        if(type == Type.BOMB)
            if(soldier instanceof StrategoMiner)
                comp = -1;
            else
                comp = 1;
        return comp;
        }
    
    /**
     * Makes this <code>StrategoSoldier</code> and another <code>StrategoSoldier</code>
     * enter a conflict and redraw them according to who died.
     * <p>
     * Will display to the user what the results of the conflict were and then 
     * redraw the soldiers according to who defeated who or if both died.
     * </p>
     * @param o The soldier to have a conflict with the
     * @param location
     */
    public void soldierConflict(Object o, Coordinate location, 
            boolean netConflict)
        {
        StrategoSoldier soldier = (StrategoSoldier)o;
        int comp = compareTo(o);
        String str1 = netConflict ? "His " : "Your ", str2 = netConflict 
                ? "your " : "opponent's ";
        if(comp < 0)
            {
            StrategoIO.infoMsg(str1 + name.trim() + " was defeated by " + str2 +
                    soldier.name.trim(), "Conflict Result");
            this.erase();
            this.remove();
            soldier.erase();
            soldier.cd = location;
            soldier.draw(netConflict ? true : false);
            }
        if(comp > 0)
            {
            StrategoIO.infoMsg(str1 + name.trim() + " defeated " + str2 +
                    soldier.name.trim(), "Conflict Result");
            this.erase();
            this.cd = location;
            soldier.erase();
            soldier.remove();
            this.draw(netConflict ? false : true);
            }
        if(comp == 0)
            {
            StrategoIO.infoMsg("Both your " + name.trim() + " and your opponents " +
                    soldier.name.trim() + " were defeated", "Conflict Result");
            soldier.erase();
            this.erase();
            soldier.remove();
            this.remove();
            }
        }
    
    public void setCoordinate(Coordinate a) throws NullPointerException
        {
        if (a == null)
            throw new NullPointerException("Null Coordinate");
        cd = a;
        }
    
    /**
     * Will draw this is soldier either with or without its name
     * @param showName
     *      If true will draw this soldier with its name else it will draw it 
     * without a name
     */
    public void draw(boolean showName)        
        {
        if (cd == null)
            return;
        StrategoGraphicLib lib = board.getLib();
        Color formerCol = lib.getPenColor();
        int penW = lib.getPenWidth();
        // saves the past settings
        lib.setPenColor(col);
        lib.setPenWidth(Coordinate.MAX_PEN_WIDTH);
        // inputs the settings to draw the circle
        lib.fillCircle(cd.getX(), cd.getY(), 
                Coordinate.DEFAULT_RADIUS);
        //draws the Stratego piece
        lib.setPenColor(Color.BLACK);
        lib.setPenWidth(Coordinate.MIN_PEN_WIDTH);
        //sets the pen to black and small to drawString
        if (showName)
        lib.drawString(cd.getX() - Coordinate.DEFAULT_RADIUS
                , cd.getY(), getName());
        //sets the name
        lib.setPenColor(formerCol);
        lib.setPenWidth(penW);
        // turns the colors back to what they were before
        }
    
    /**
     * Erases this soldier off of the board and sets its coordinate location to 
     * null.
     */
    public void erase()
        {
        StrategoGraphicLib lib = board.getLib();
        Color formerCol = lib.getPenColor(); 
        int penW = lib.getPenWidth();
        lib.setPenColor(Color.WHITE);
        lib.setPenWidth(Coordinate.MAX_PEN_WIDTH);
        lib.fillCircle(cd.getX(), cd.getY(), Coordinate.DEFAULT_RADIUS);
        lib.setPenColor(formerCol);
        lib.setPenWidth(penW);
        }
    
    /**
     * Removes refernences to this soldier from all locations
     */
    private boolean remove() 
        {
        board.getLocalPlayer().removeSoldier(this);
        board.getNetworkPlayer().removeSoldier(this);
        ArrayList<StrategoSoldier> full = board.getSoldiers();
        for(int x = 0; x < full.size(); x++)
            if(full.get(x) == this)
                full.remove(x);
        return true;
        }
    
    /**
     * Sets this soldier to a different color.
     * 
     * @param colo
     *          The new color to which this soldier is being set to.
     */
    public void setColor(Color colo)
        {
        if (col == null && colo == null)
            {
            col = Color.BLACK;
            return;
            }
        if (colo != null)
            col = colo;
        }
    
    protected StrategoSoldier getSoldier(Coordinate cd, ArrayList<StrategoSoldier>
            ss)
        {
        for (StrategoSoldier s : ss)
            if (s.getCoordinate().equals(cd))
                return s;
        return null;
        }
    
    /**
     * Gets the possible move locations of this <code>StrategoSoldier</code>
     * 
     * @param network
     *      The <code>ArrayList</code> of soldiers of the network player
     * @param local
     *      The <code>ArrayList</code> of soldiers of the local player
     * @return
     *      The <code>ArrayList</code> of coordinates that this soldier can move
     * to
     */
    public ArrayList<Coordinate> getMoveCoordinates(ArrayList<StrategoSoldier>
            network, ArrayList<StrategoSoldier> local)
        {
        ArrayList<Coordinate> c = new ArrayList<Coordinate>();
        int here = getCoordinate().hashCode();
        System.out.println("The hash of this soldier is " + here);
        StrategoSoldier s = null;
        Coordinate north = Coordinate.getCoorFromHash(here - 10);
        s = getSoldier(north, local);
        if (north != null && s == null)
            c.add(north);
        //s = null;
        Coordinate south = Coordinate.getCoorFromHash(here + 10);
        s = getSoldier(south, local);
        if (south != null && s == null)
            c.add(south);
        //s = null;
        Coordinate west = Coordinate.getCoorFromHash(here - 1);
        s = getSoldier(west, local);
        if (west != null && s == null && here/10 == west.hashCode()/10)
            c.add(west);
        //s = null;
        Coordinate east = Coordinate.getCoorFromHash(here + 1);
        s = getSoldier(east, local);
        if (east != null && s == null && here/10 == east.hashCode()/10)
            c.add(east);
        return c;
        // override the function in scout so it has a longer range
        }

    /**
     * Returns an ArrayList of StrategoSoldiers with null locations for the
     * the players to store.
     * 
     * @param local
     *      Boolean that will make the soldiers cyan if true and red if false.
     * @param bd
     *      The board containing the players who will be using the sets.
     * @return
     *      An ArrayList of StrategoSoldiers with null coordinates.
     */
    public static ArrayList<StrategoSoldier> getSoldierCompleteSet(boolean local
            , Board bd)
        {
        Color playerCol = null;
        if (local)
            playerCol = LOCAL_PLAYER_COLOR;
        else
            playerCol = NETWORK_PLAYER_COLOR;
        ArrayList<StrategoSoldier> solds = new ArrayList<StrategoSoldier>(
                DEFAULT_PIECES);
        //Adding Marshal
        solds.add(new StrategoMarshal(null, playerCol, bd));
        //Adding General
        solds.add(new StrategoGeneral(null, playerCol, bd));
        //Adding 2 Colonels
        solds.add(new StrategoColonel(null, playerCol, bd));
        solds.add(new StrategoColonel(null, playerCol, bd));
        //Adding 3 majors
        for (int i = 0; i < 3; i++)
            solds.add(new StrategoMajor(null, playerCol, bd));
        //Adding 4 Captains
        for (int i = 0; i < 4; i++)
            solds.add(new StrategoCaptain(null, playerCol, bd));
        //Addings 4 Lieutenants
        for (int i = 0; i < 4; i++)
            solds.add(new StrategoLieutenant(null, playerCol, bd));
        //Adding 4 Sergeants
        for (int i = 0; i < 4; i++)
            solds.add(new StrategoSergeant(null, playerCol, bd));
        //Adding 5 Minors
        for (int i = 0; i < 5; i++)
            solds.add(new StrategoMiner(null, playerCol, bd));
        //Adding 8 Scouts
        for (int i = 0; i < 8; i++)
            solds.add(new StrategoScout(null, playerCol, bd));
        //Adding a spy
        solds.add(new StrategoSpy(null, playerCol, bd));
        //Adding 6 bombs
        for (int i = 0; i < 6; i++)
            solds.add(new StrategoBomb(null, playerCol, bd));
        //Addings 1 flag
        solds.add(new StrategoFlag(null, playerCol, bd));
        if (solds.size() == DEFAULT_PIECES)
            return solds;
        else
            throw new IndexOutOfBoundsException("There must be 40 pieces" +
                    "in the default ArrayList");
        }
    
    public Coordinate getCoordinate()
        {
        return cd;
        }
    
    @Override
    public String toString()
        {
        StringBuffer status = new StringBuffer();
        status.append("Stratego Soldier Status\n");
        status.append("I am a " + name.trim() + ".\n");
        status.append("Piece Value: " + pieceVal + "\n");
        status.append("Stratego Soldier Coordinate Status:\n");
        status.append("" + cd);
        status.append("Battles Counter: " + numBattles + "\n");
        status.append("Color: " + col + ".\n");
        return status.toString();
        }
    
    public String getName()
        {
        return name;
        }
    
    public int getVal()
        {
        return pieceVal;
        }
    
    public Type getType()
        {
        return type;
        }
               
    //Constant ints
    public static final int DEFAULT_PIECES = 40;
          
    //Constant Soldier Color Values
    public static final Color LOCAL_PLAYER_COLOR = Color.CYAN; // "netbuddy"
    public static final Color NETWORK_PLAYER_COLOR = Color.RED; // host
    }

class StrategoMarshal extends StrategoSoldier
    {
    public StrategoMarshal(Coordinate cd, Color cl, Board bd)
        {
        super(1, "  Marshal", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoMarshal(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoGeneral extends StrategoSoldier
    {
    public StrategoGeneral(Coordinate cd, Color cl, Board bd)
        {
        super(2, "  General", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoGeneral(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoColonel extends StrategoSoldier
    {
    public StrategoColonel(Coordinate cd, Color cl, Board bd)
        {
        super(3, "  Colonel", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoColonel(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoMajor extends StrategoSoldier
    {
    public StrategoMajor(Coordinate cd, Color cl, Board bd)
        {
        super(4, "     Major", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoMajor(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoCaptain extends StrategoSoldier
    {
    public StrategoCaptain(Coordinate cd, Color cl, Board bd)
        {
        super(5, "  Captain", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoCaptain(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoLieutenant extends StrategoSoldier
    {
    public StrategoLieutenant(Coordinate cd, Color cl, Board bd)
        {
        super(6, "Lieutenant", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoLieutenant(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoSergeant extends StrategoSoldier
    {
    public StrategoSergeant(Coordinate cd, Color cl, Board bd)
        {
        super(7, " Sergeant", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoSergeant(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoScout extends StrategoSoldier
    {
    public StrategoScout(Coordinate cd, Color cl, Board bd)
        {
        super(9, "   Scout", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoScout(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    
    @Override
    public ArrayList<Coordinate> getMoveCoordinates(ArrayList<StrategoSoldier>
            network, ArrayList<StrategoSoldier> local)
        {
        ArrayList<Coordinate> coors = new ArrayList<Coordinate>();
        if (getCoordinatesUntil(cd, network, local, 0) != null)
            for (Coordinate move : getCoordinatesUntil(this.cd, network, local, 0))
                coors.add(move);
        if (getCoordinatesUntil(cd, network, local, 1) != null)
            for (Coordinate move : getCoordinatesUntil(this.cd, network, local, 1))
                coors.add(move);
        if (getCoordinatesUntil(cd, network, local, 2) != null)
            for (Coordinate move : getCoordinatesUntil(this.cd, network, local, 2))
                coors.add(move);
        if (getCoordinatesUntil(cd, network, local, 3) != null)
            for (Coordinate move : getCoordinatesUntil(this.cd, network, local, 3))
                coors.add(move);
        return coors;
        }
    
    private ArrayList<Coordinate> getCoordinatesUntil(Coordinate init,
            ArrayList<StrategoSoldier> network, ArrayList<StrategoSoldier> local,
            int direction)
            // 0 for north, 1 for south, 2 for east, 3 for west
        {
        ArrayList<Coordinate> ret = new ArrayList<Coordinate>();
        int sameRow = 0;
        switch(direction)
            {
            case 0:
                init = Coordinate.getCoorFromHash(init.hashCode()
                        - 10);
                while (!isThere(init, network, local))
                    {
                    ret.add(init);
                    init = Coordinate.getCoorFromHash(init.hashCode()
                        - 10);
                    }
                ret.add(init);
                return ret;
            case 1:
                init = Coordinate.getCoorFromHash(init.hashCode()
                        + 10);
                while (!isThere(init, network, local))
                    {
                    ret.add(init);
                    init = Coordinate.getCoorFromHash(init.hashCode()
                        + 10);
                    }
                ret.add(init);
                return ret;
            case 2:
                sameRow = init.hashCode()/10;
                init = Coordinate.getCoorFromHash(init.hashCode()
                        + 1);
                while (!isThere(init, network, local) && init.hashCode()
                        /10 == sameRow)
                    {
                    ret.add(init);
                    init = Coordinate.getCoorFromHash(init.hashCode()
                        + 1);
                    }
                ret.add(init);
                return ret;
            case 3:
                sameRow = init.hashCode()/10;
                init = Coordinate.getCoorFromHash(init.hashCode()
                        - 1);
                while (!isThere(init, network, local) && init.hashCode()/10 ==
                        sameRow)
                    {
                    ret.add(init);
                    init = Coordinate.getCoorFromHash(init.hashCode()
                        - 1);
                    }
                ret.add(init);
                return ret;
            default:
                return null;
            }
        }
    
    private boolean isThere(Coordinate there, ArrayList<StrategoSoldier> network, 
            ArrayList<StrategoSoldier> local)
        {
        if (there == null)
            return true;
        for (StrategoSoldier ss: network)
            if (ss.getCoordinate().equals(there))
                return true;
        for (StrategoSoldier ss: local)
            if (ss.getCoordinate().equals(there))
                return true;
        if (Coordinate.getCoorFromHash(41).equals(cd))
            return true;
        if (Coordinate.getCoorFromHash(42).equals(cd))
            return true;
        if (Coordinate.getCoorFromHash(51).equals(cd))
            return true;
        if (Coordinate.getCoorFromHash(52).equals(cd))
            return true;
        if (Coordinate.getCoorFromHash(47).equals(cd))
            return true;
        if (Coordinate.getCoorFromHash(48).equals(cd))
            return true;
        if (Coordinate.getCoorFromHash(57).equals(cd))
            return true;
        if (Coordinate.getCoorFromHash(58).equals(cd))
            return true;
        return false;
        }
    }

class StrategoMiner extends StrategoSoldier
    {
    public StrategoMiner(Coordinate cd, Color cl, Board bd)
        {
        super(9, "   Miner", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoMiner(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoSpy extends StrategoSoldier
    {
    public StrategoSpy(Coordinate cd, Color cl, Board bd)
        {
        super(10, "    Spy", Type.SOLDIER, cd, cl, bd);
        }
    
    public StrategoSpy(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoFlag extends StrategoSoldier
    {
    public StrategoFlag(Coordinate cd, Color cl, Board bd)
        {
        super("    Flag", Type.FLAG, cd, cl, bd);
        }
    
    public StrategoFlag(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }

class StrategoBomb extends StrategoSoldier
    {
    public StrategoBomb(Coordinate cd, Color cl, Board bd)
        {
        super("     Bomb", Type.BOMB, cd, cl, bd);
        }
    
    public StrategoBomb(int row, int col, Color cl, Board bd)
        {
        this(Coordinate.getCoorFromHash(row * 10 + col), cl, bd);
        }
    }


